package com.atguigu.tingshu.order.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.album.client.AlbumFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.common.utils.ThrowUtil;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderDerateService;
import com.atguigu.tingshu.order.service.OrderDetailService;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.order.strategy.OrderBuildStrategyFactory;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.account.AccountFeignClient;
import com.atguigu.tingshu.vo.account.AccountDeductVo;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Service;

import jakarta.annotation.PostConstruct;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({ "all" })
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private OrderDerateMapper orderDerateMapper;

    @Autowired
    private OrderDetailService orderDetailService;

    @Autowired
    private OrderDerateService orderDerateService;

    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private AlbumFeignClient albumFeignClient;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private OrderBuildStrategyFactory orderBuildStrategyFactory;

    @Autowired
    private AccountFeignClient accountFeignClient;

    /**
     * Lua脚本：检查并删除流水号
     */
    private DefaultRedisScript<Long> checkAndDeleteTradeNoScript;

    /**
     * 初始化Lua脚本
     */
    @PostConstruct
    public void init() {
        checkAndDeleteTradeNoScript = new DefaultRedisScript<>();
        checkAndDeleteTradeNoScript.setResultType(Long.class);
        checkAndDeleteTradeNoScript.setScriptSource(
                new ResourceScriptSource(new ClassPathResource("lua/check_and_delete_trade_no.lua")));
        log.info("Lua脚本初始化完成");
    }

    @Override
    public OrderInfoVo trade(TradeVo tradeVo) {
        log.info("订单确认 - tradeVo: {}", tradeVo);
        // 获取当前登录用户ID
        Long userId = AuthContextHolder.getUserId();
        // 使用策略模式处理不同类型的订单
        String itemType = tradeVo.getItemType();
        OrderInfoVo orderInfoVo = orderBuildStrategyFactory.getStrategy(itemType)
                .buildOrderInfo(tradeVo, userId);
        // 生成时间戳和签名
        generateTimestampAndSign(orderInfoVo, userId, itemType, tradeVo.getItemId());
        log.info("订单确认完成 - orderInfoVo: {}", orderInfoVo);
        // 测试校验签名
        // String sign = orderInfoVo.getSign();
        // orderInfoVo.setPayWay(null);
        // orderInfoVo.setSign(null);
        // SignHelper.verifySign(BeanUtil.beanToMap(orderInfoVo, false, true), sign);
        return orderInfoVo;
    }

    /**
     * 生成时间戳、签名和流水号
     *
     * @param orderInfoVo 订单信息
     * @param userId      用户ID
     * @param itemType    付款项目类型
     * @param itemId      付款项目ID
     */
    private void generateTimestampAndSign(OrderInfoVo orderInfoVo, Long userId, String itemType, Long itemId) {
        log.info("生成时间戳和签名 - userId: {}, itemType: {}", userId, itemType);
        // 1. 生成时间戳（当前时间戳）
        long timestamp = System.currentTimeMillis();
        orderInfoVo.setTimestamp(timestamp);
        // 2. 生成流水号：业务逻辑:userId:付款类型:itemId
        String tradeNo = RedisConstant.BUSINESS_PREFIX + userId + ":" + itemType + ":" + itemId;
        orderInfoVo.setTradeNo(tradeNo);
        log.info("生成流水号 - tradeNo: {}", tradeNo);
        // 3. 将流水号存入Redis，设置过期时间（用于防止重复提交）
        redisTemplate.opsForValue().set(
                tradeNo,
                "1", // 值只是标记存在，不再存储orderNo
                RedisConstant.ORDER_TRADE_EXPIRE,
                TimeUnit.MINUTES);
        log.info("流水号已存入Redis - tradeNo: {}, 过期时间: {}分钟",
                tradeNo, RedisConstant.ORDER_TRADE_EXPIRE);
        // 4. 构建签名参数Map
        Map<String, Object> signMap = BeanUtil.beanToMap(orderInfoVo, false, true);
        // 5. 生成签名
        String sign = SignHelper.generateSign(signMap);
        orderInfoVo.setSign(sign);
        log.info("时间戳和签名生成完成 - timestamp: {}, sign: {}", timestamp, sign);
    }

    @Override
    public String submitOrder(OrderInfoVo orderInfoVo) {
        log.info("提交订单 - orderInfoVo: {}", orderInfoVo);
        // 1. 获取当前登录用户ID
        Long userId = AuthContextHolder.getUserId();
        // 2. 使用Lua脚本原子性检查并删除流水号
        String tradeNo = orderInfoVo.getTradeNo();
        Long result = redisTemplate.execute(
                checkAndDeleteTradeNoScript,
                Collections.singletonList(tradeNo));
        ThrowUtil.throwIf(result == null || result == 0, "流水号不存在或已过期");
        log.info("流水号校验通过并已删除 - tradeNo: {}", tradeNo);
        // 3. 校验签名
        String sign = orderInfoVo.getSign();
        Map<String, Object> signMap = BeanUtil.beanToMap(orderInfoVo, false, true);
        signMap.remove("sign");// 移除sign字段
        signMap.remove("payWay");// 移除付款方式字段
        SignHelper.verifySign(signMap, sign);
        log.info("签名校验通过");
        // 4. 生成订单编号：时间 + 雪花算法
        String orderNo = DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss") + IdUtil.getSnowflakeNextId();
        log.info("生成订单编号 - orderNo: {}", orderNo);
        // 5. 构建订单对象
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setUserId(userId);
        orderInfo.setOrderNo(orderNo);
        orderInfo.setItemType(orderInfoVo.getItemType());
        orderInfo.setPayWay(orderInfoVo.getPayWay());
        orderInfo.setOriginalAmount(orderInfoVo.getOriginalAmount());
        orderInfo.setDerateAmount(orderInfoVo.getDerateAmount());
        orderInfo.setOrderAmount(orderInfoVo.getOrderAmount());
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID); // 设置订单状态为未支付
        // 构建订单标题
        List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
        if (CollUtil.isNotEmpty(orderDetailVoList)) {
            OrderDetailVo firstDetail = orderDetailVoList.get(0);
            if (orderDetailVoList.size() == 1) {
                orderInfo.setOrderTitle(firstDetail.getItemName());
            } else {
                orderInfo.setOrderTitle(firstDetail.getItemName() + "等" + orderDetailVoList.size() + "项");
            }
        }
        // 6. 插入订单表
        orderInfoMapper.insert(orderInfo);
        Long orderId = orderInfo.getId();
        log.info("订单插入成功 - orderId: {}, orderNo: {}", orderId, orderNo);
        // 7. 使用Stream流批量插入订单明细表
        if (CollUtil.isNotEmpty(orderDetailVoList)) {
            List<OrderDetail> orderDetailList = orderDetailVoList.stream()
                    .map(detailVo -> {
                        OrderDetail orderDetail = new OrderDetail();
                        orderDetail.setOrderId(orderId);
                        orderDetail.setItemId(detailVo.getItemId());
                        orderDetail.setItemName(detailVo.getItemName());
                        orderDetail.setItemUrl(detailVo.getItemUrl());
                        orderDetail.setItemPrice(detailVo.getItemPrice());
                        return orderDetail;
                    })
                    .collect(Collectors.toList());
            orderDetailService.saveBatch(orderDetailList);
            log.info("订单明细批量插入成功 - 明细数量: {}", orderDetailList.size());
        }
        // 8. 使用Stream流批量插入订单减免表
        List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();
        if (CollUtil.isNotEmpty(orderDerateVoList)) {
            List<OrderDerate> orderDerateList = orderDerateVoList.stream()
                    .map(derateVo -> {
                        OrderDerate orderDerate = new OrderDerate();
                        orderDerate.setOrderId(orderId);
                        orderDerate.setDerateType(derateVo.getDerateType());
                        orderDerate.setDerateAmount(derateVo.getDerateAmount());
                        orderDerate.setRemarks(derateVo.getRemarks());
                        return orderDerate;
                    })
                    .collect(Collectors.toList());
            orderDerateService.saveBatch(orderDerateList);
            log.info("订单减免明细批量插入成功 - 减免明细数量: {}", orderDerateList.size());
        }
        // 9. 支付步骤：判断是余额支付还是微信支付
        String payWay = orderInfo.getPayWay();
        if (SystemConstant.ORDER_PAY_ACCOUNT.equals(payWay)) {
            // 余额支付
            log.info("开始处理余额支付 - orderNo: {}, orderAmount: {}", orderNo, orderInfo.getOrderAmount());
            // 9.1 调用账户服务预扣减
            AccountDeductVo accountDeductVo = new AccountDeductVo();
            accountDeductVo.setOrderNo(orderNo);
            accountDeductVo.setUserId(userId);
            accountDeductVo.setAmount(orderInfo.getOrderAmount());
            accountDeductVo.setContent("订单支付:“" + orderInfo.getOrderTitle() + "”");
            Result<?> deductResult = accountFeignClient.checkAndDeduct(accountDeductVo);
            ThrowUtil.throwIf(deductResult == null, "调用账户服务失败");
            ThrowUtil.throwIf(deductResult.getCode() != 200, "余额扣减失败: " + deductResult.getMessage());
            log.info("余额扣减成功 - orderNo: {}", orderNo);
            // 9.2 调用用户服务保存购买记录
            UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
            userPaidRecordVo.setOrderNo(orderNo);
            userPaidRecordVo.setUserId(userId);
            userPaidRecordVo.setItemType(orderInfo.getItemType());
            // 从订单明细中提取itemId列表
            List<Long> itemIdList = orderDetailVoList.stream()
                    .map(OrderDetailVo::getItemId)
                    .collect(Collectors.toList());
            userPaidRecordVo.setItemIdList(itemIdList);
            Result<Void> savePaidRecordResult = userFeignClient.savePaidRecord(userPaidRecordVo);
            ThrowUtil.throwIf(savePaidRecordResult == null, "调用用户服务失败");
            ThrowUtil.throwIf(savePaidRecordResult.getCode() != 200, "保存购买记录失败: " + savePaidRecordResult.getMessage());
            log.info("保存购买记录成功 - orderNo: {}", orderNo);
            // 9.3 更新订单状态为已支付
            OrderInfo updateOrder = new OrderInfo();
            updateOrder.setId(orderId);
            updateOrder.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
            orderInfoMapper.updateById(updateOrder);
            log.info("订单状态更新为已支付 - orderNo: {}", orderNo);
        } else if (SystemConstant.ORDER_PAY_WAY_WEIXIN.equals(payWay)) {
            // 微信支付
            // TODO: 微信支付功能待实现
            log.info("TODO: 微信支付功能待实现 - orderNo: {}, orderAmount: {}",
                    orderNo, orderInfo.getOrderAmount());
        } else {
            log.warn("未知的支付方式 - payWay: {}", payWay);
        }
        log.info("订单提交完成 - orderNo: {}", orderNo);
        return orderNo;
    }
}
